package handling;

import client.MapleClient;
import configs.Game;
import constants.ServerConfig;
import gui.Start;
import handling.cashshop.CashShopServer;
import handling.cashshop.handler.CashShopOperation;
import handling.cashshop.handler.MTSOperation;
import handling.channel.ChannelServer;
import handling.channel.handler.*;
import handling.login.LoginServer;
import handling.login.handler.CharLoginHandler;
import handling.mina.MaplePacketDecoder;
import handling.mina.MaplePacketDecoder.DecoderState;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import scripting.NPCScriptManager;
import server.MTSStorage;
import server.Randomizer;
import tools.*;
import tools.data.ByteArrayByteStream;
import tools.data.LittleEndianAccessor;
import tools.packet.LoginPacket;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;

@Slf4j
public class MapleServerHandler extends ChannelInboundHandlerAdapter {
    private int channel;
    private List<String> BlockedIP;
    private Map<String, Pair<Long, Byte>> tracker;
    private static EnumSet<RecvPacketOpcode> BLOCKED;

    public MapleServerHandler(int channel) {
        this.BlockedIP = new ArrayList<>();
        this.tracker = new ConcurrentHashMap<>();
        this.channel = channel;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        String address = ctx.channel().remoteAddress().toString().split(":")[0];
        if (Start.ConfigValuesMap.get("管理员独占登录") > 0) {
            System.out.println("[登录服务] " + address + " 已连接");
        }
        if (this.BlockedIP.contains(address)) {
            FileoutputUtil.logToFile("logs/Data/连接断线.txt", "\r\n " + FileoutputUtil.NowTime() + " IP: " + address + " 连接断线1");
            ctx.channel().close();
            return;
        }
        Pair<Long, Byte> track = this.tracker.get(address);
        byte count;
        if (track == null) {
            count = 1;
        } else {
            count = track.right;
            long difference = System.currentTimeMillis() - track.left;
            if (difference < 2000L) {
                ++count;
            } else if (difference > 20000L) {
                count = 1;
            }
            if (count >= 10) {
                this.BlockedIP.add(address);
                this.tracker.remove(address);
                FileoutputUtil.logToFile("logs/Data/连接断线.txt", "\r\n " + FileoutputUtil.NowTime() + " IP: " + address + " 连接断线2");
                ctx.channel().close();
                return;
            }
        }
        this.tracker.put(address, new Pair<>(System.currentTimeMillis(), count));
        if (this.channel == -10) {
            if (CashShopServer.isShutdown()) {
                ctx.channel().close();
                return;
            }
        } else if (this.channel == 0) {
            if (LoginServer.isShutdown()) {
                ctx.channel().close();
                return;
            }
        } else {
            if (this.channel <= 0) {
                System.out.println("[连结错误] 未知类型: " + this.channel);
                FileoutputUtil.logToFile("logs/Data/连接断线.txt", "\r\n " + FileoutputUtil.NowTime() + " IP: " + address + " 连接断线3");
                ctx.channel().close();
                return;
            }
            if (ChannelServer.getInstance(this.channel).isShutdown()) {
                ctx.channel().close();
                return;
            }
        }
        byte[] ivRecv = {(byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255)};
        byte[] ivSend = {(byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255), (byte) Randomizer.nextInt(255)};
        MapleClient client = new MapleClient(new MapleAESOFB(ivSend, (short) (-80)), new MapleAESOFB(ivRecv, (short) 79), ctx.channel());
        client.setChannel(this.channel);
        DecoderState decoderState = new DecoderState();
        ctx.channel().attr(MaplePacketDecoder.DECODER_STATE_KEY).set(decoderState);
        ctx.writeAndFlush(LoginPacket.getHello((short) 79, ivSend, ivRecv));
        ctx.channel().attr(MapleClient.CLIENT_KEY).set(client);
        if (Start.ConfigValuesMap.get("欢迎弹窗") > 0) {
            ctx.channel().write(MaplePacketCreator.serverNotice(1, "欢迎 (*^__^*) 来到\r\n\r\n< " + Game.服务端名字 + " >"));
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        try {
            MapleClient client = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
            if (client != null) {
                try {
                    client.disconnect(true, this.channel == -10);
                    if (this.channel == 0) {
                        client.setCanloginpw(false);
                        LoginServer.removeClient(client);
                    }
                } finally {
                    ctx.channel().close();
                    ctx.channel().attr(MapleClient.CLIENT_KEY).remove();
                }
            }
        } catch (Exception e) {
            FileoutputUtil.outError("logs/断开连接异常.txt", e);
        }
        super.channelInactive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object message) {
        LittleEndianAccessor slea = new LittleEndianAccessor(new ByteArrayByteStream((byte[]) message));
        if (slea.available() < 2L) {
            return;
        }
        MapleClient c = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        if (c == null || !c.isReceiving()) {
            return;
        }
        short header_num = slea.readShort();
        RecvPacketOpcode[] values = RecvPacketOpcode.values();
        int length = values.length;
        int i = 0;
        while (i < length) {
            RecvPacketOpcode recv = values[i];
            if (recv.getValue() == header_num) {
                if (recv.NeedsChecking() && !c.isLoggedIn()) {
                    return;
                }
                if (c.getPlayer() != null && c.isMonitored() && !MapleServerHandler.BLOCKED.contains(recv)) {
                    FilePrinter.print("Monitored/" + c.getPlayer().getName() + ".txt", recv + " (" + Integer.toHexString((int) header_num) + ") Handled: \r\n" + slea.toString() + "\r\n");
                }
                try {
                    handlePacket(recv, slea, c, this.channel == -10);
                } catch (RejectedExecutionException ignored) {
                } catch (Exception e) {
                    if (c.getPlayer() != null && c.getPlayer().isShowErr()) {
                        c.getPlayer().showInfo("数据包异常", true, "包头:" + recv.name() + "(0x" + Integer.toHexString((int) header_num).toUpperCase() + ")");
                    }
                    FileoutputUtil.outputFileError("logs\\Except\\Log_Code_Except.txt", e, false);
                    FileoutputUtil.outputFileError("logs\\Except\\Log_Packet_Except.txt", e);
                    FileoutputUtil.log("logs\\Except\\Log_Packet_Except.txt", "Packet: " + (int) header_num + "\r\n包头:" + recv.name() + "(0x" + Integer.toHexString((int) header_num).toUpperCase() + ")\r\n" + slea.toString(true));
                }
                return;
            } else {
                ++i;
            }
        }
        if (ServerConfig.LOG_PACKETS) {
            byte[] packet = slea.read((int) slea.available());
            FileoutputUtil.log("logs\\服务端数据包_未知.txt", "发现未知服务端数据包 - (包头:0x" + Integer.toHexString(header_num) + ")" + ":\r\n" + HexTool.toString(packet) + "\r\n" + HexTool.toStringFromAscii(packet));
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object status) throws Exception {
        MapleClient client = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        if (client != null) {
            client.sendPing();
        } else {
            ctx.channel().close();
            System.out.println("netty检测心跳掉线。");
        }
        super.userEventTriggered(ctx, status);
    }

    public static void handlePacket(RecvPacketOpcode header, LittleEndianAccessor slea, MapleClient c, boolean cs) {
        switch (header) {
            case CLIENT_LOGOUT, HELLO_CHANNEL, STRANGE_DATA, CANCEL_DEBUFF, COCONUT, CLIENT_FEEDBACK, NO_CB, NO_E0, NO_69 -> {
            }
            case PONG -> c.pongReceived();
            case HELLO_LOGIN -> {
                if (slea.available() >= 5L) {
                    long avaible = slea.available();
                    String debug = "";
                    if (c != null) {
                        debug = debug + c.getAccountName() + "_";
                    }
                    FilePrinter.print(debug + "38Logs.txt", HexTool.toStringFromAscii(slea.read((int) avaible)), true);
                }
            }
            case LOGIN_PASSWORD -> CharLoginHandler.handleLogin(slea, c);
            case SERVERLIST_REQUEST -> CharLoginHandler.ServerListRequest(c);
            case CHARLIST_REQUEST -> CharLoginHandler.CharlistRequest(slea, c);
            case SERVERSTATUS_REQUEST -> CharLoginHandler.ServerStatusRequest(c);
            case CHECK_CHAR_NAME -> CharLoginHandler.checkCharName(slea.readMapleAsciiString(), c);
            case CREATE_CHAR -> CharLoginHandler.handleCreateCharacter(slea, c);
            case DELETE_CHAR -> CharLoginHandler.handleDeleteCharacter(slea, c);
            case CHAR_SELECT -> CharLoginHandler.handleSecectCharacter(slea, c);
            case SET_GENDER -> CharLoginHandler.SetGenderRequest(slea, c);
            case CHANGE_CHANNEL -> InterServerHandler.ChangeChannel(slea, c, c.getPlayer());
            case QUEST_MAPLEPOINTS -> InventoryHandler.QuestMaplepoints(slea, c, c.getPlayer());
            case PLAYER_LOGGEDIN -> {
                int playerid = slea.readInt();
                if (cs) {
                    CashShopOperation.EnterCashShop(playerid, c);
                    break;
                }
                InterServerHandler.LoggedIn(playerid, c);
            }
            case ENTER_CASH_SHOP -> {
                if (c.getPlayer().getMapId() != 980000010 & c.getPlayer().getMapId() != 980000020 & c.getPlayer().getMapId() != 980000100 & c.getPlayer().getMapId() != 980000101 & c.getPlayer().getMapId() != 980000102 & c.getPlayer().getMapId() != 980000103 & c.getPlayer().getMapId() != 980000104 & c.getPlayer().getMapId() != 980000200 & c.getPlayer().getMapId() != 980000201 & c.getPlayer().getMapId() != 980000202 & c.getPlayer().getMapId() != 980000203 & c.getPlayer().getMapId() != 980000204 & c.getPlayer().getMapId() != 980000300 & c.getPlayer().getMapId() != 980000301 & c.getPlayer().getMapId() != 980000302 & c.getPlayer().getMapId() != 980000303 & c.getPlayer().getMapId() != 980000304 & c.getPlayer().getMapId() != 980000400 & c.getPlayer().getMapId() != 980000401 & c.getPlayer().getMapId() != 980000402 & c.getPlayer().getMapId() != 980000403 & c.getPlayer().getMapId() != 980000404 & c.getPlayer().getMapId() != 980000400 & c.getPlayer().getMapId() != 980000501 & c.getPlayer().getMapId() != 980000502 & c.getPlayer().getMapId() != 980000503 & c.getPlayer().getMapId() != 980000504 & c.getPlayer().getMapId() != 980000600 & c.getPlayer().getMapId() != 980000601 & c.getPlayer().getMapId() != 980000602 & c.getPlayer().getMapId() != 980000603 & c.getPlayer().getMapId() != 980000604) {
                    c.getSession().writeAndFlush(MaplePacketCreator.enableActions());
                    NPCScriptManager.getInstance().start(c, 9010000, "进入商城");
                    break;
                }
                c.getPlayer().dropMessage(5, "该地图无法进入商城。");
                c.getSession().writeAndFlush(MaplePacketCreator.enableActions());
            }
            case ENTER_MTS -> {
                if (c.getPlayer().getMobVac() || c.getPlayer().getItemVac()) {
                    c.getPlayer().dropMessage(1, "开着挂机的时候不能操作");
                    c.getSession().writeAndFlush(MaplePacketCreator.enableActions());
                    break;
                }
                if (c.getPlayer().getMapId() == 910010300 || c.getPlayer().getMapId() == 910010100 || (c.getPlayer().getMapId() > 211060000 && c.getPlayer().getMapId() <= 211061000)) {
                    c.getPlayer().dropMessage(5, "该地图无法使用拍卖功能。");
                    c.getSession().writeAndFlush(MaplePacketCreator.enableActions());
                    break;
                }
                c.getSession().writeAndFlush(MaplePacketCreator.enableActions());
                NPCScriptManager.getInstance().start(c, 9010000, "聚合功能");
            }
            case MOVE_PLAYER -> PlayerHandler.MovePlayer(slea, c, c.getPlayer());
            case CHAR_INFO_REQUEST -> {
                c.getPlayer().updateTick(slea.readInt());
                PlayerHandler.CharInfoRequest(slea.readInt(), c, c.getPlayer());
            }
            case CLOSE_RANGE_ATTACK -> PlayerHandler.closeRangeAttack(slea, c, c.getPlayer(), false);
            case RANGED_ATTACK -> PlayerHandler.rangedAttack(slea, c, c.getPlayer());
            case MAGIC_ATTACK -> PlayerHandler.MagicDamage(slea, c, c.getPlayer());
            case SPECIAL_MOVE -> PlayerHandler.SpecialMove(slea, c, c.getPlayer());
            case PASSIVE_ENERGY -> PlayerHandler.closeRangeAttack(slea, c, c.getPlayer(), true);
            case FACE_EXPRESSION -> PlayerHandler.ChangeEmotion(slea.readInt(), c.getPlayer());
            case TAKE_DAMAGE -> PlayerHandler.TakeDamage(slea, c, c.getPlayer());
            case HEAL_OVER_TIME -> PlayerHandler.Heal(slea, c.getPlayer());
            case CANCEL_BUFF -> PlayerHandler.CancelBuffHandler(slea.readInt(), c.getPlayer());
            case CANCEL_ITEM_EFFECT -> PlayerHandler.CancelItemEffect(slea.readInt(), c.getPlayer());
            case USE_CHAIR -> PlayerHandler.UseChair(slea.readInt(), c, c.getPlayer());
            case SHOW_EXP_CHAIR -> PlayerHandler.ShowExpChair(slea, c);
            case CANCEL_CHAIR -> PlayerHandler.CancelChair(slea.readShort(), c, c.getPlayer());
            case USE_ITEMEFFECT, WHEEL_OF_FORTUNE -> PlayerHandler.UseItemEffect(slea.readInt(), c, c.getPlayer());
            case SKILL_EFFECT -> PlayerHandler.SkillEffect(slea, c.getPlayer());
            case MESO_DROP -> {
                c.getPlayer().updateTick(slea.readInt());
                PlayerHandler.DropMeso(slea.readInt(), c.getPlayer());
            }
            case MONSTER_BOOK_COVER -> PlayerHandler.ChangeMonsterBookCover(slea.readInt(), c, c.getPlayer());
            case CHANGE_KEYMAP -> PlayerHandler.ChangeKeymap(slea, c.getPlayer());
            case CHANGE_MAP -> {
                if (cs) {
                    CashShopOperation.LeaveCashShop(slea, c, c.getPlayer());
                    break;
                }
                PlayerHandler.ChangeMap(slea, c, c.getPlayer());
            }
            case CHANGE_MAP_SPECIAL -> {
                slea.skip(1);
                PlayerHandler.ChangeMapSpecial(slea.readMapleAsciiString(), c, c.getPlayer());
            }
            case USE_INNER_PORTAL -> {
                slea.skip(1);
                PlayerHandler.InnerPortal(slea, c, c.getPlayer());
            }
            case TROCK_ADD_MAP -> PlayerHandler.TrockAddMap(slea, c, c.getPlayer());
            case LIE_DETECTOR_SKILL -> PlayersHandler.LieDetector(slea, c, c.getPlayer(), false);
            case LIE_DETECTOR -> PlayersHandler.LieDetector(slea, c, c.getPlayer(), true);
            case ChatRoom_SYSTEM -> PlayersHandler.ChatRoomHandler(slea, c);
            case Use_ViciousHammer -> InventoryHandler.UseViciousHammer(slea, c);
            case LIE_DETECTOR_RESPONSE -> PlayersHandler.LieDetectorResponse(slea, c);
            case LIE_DETECTOR_REFRESH -> {
            }
            case ARAN_COMBO -> PlayerHandler.AranCombo(c, c.getPlayer(), 1);
            case SKILL_MACRO -> PlayerHandler.ChangeSkillMacro(slea, c.getPlayer());
            case ITEM_BAOWU -> InventoryHandler.UsePenguinBox(slea, c);
            case ITEM_SUNZI -> InventoryHandler.SunziBF(slea, c);
            case GIVE_FAME -> PlayersHandler.GiveFame(slea, c, c.getPlayer());
            case TRANSFORM_PLAYER -> PlayersHandler.TransformPlayer(slea, c, c.getPlayer());
            case NOTE_ACTION -> PlayersHandler.Note(slea, c.getPlayer());
            case USE_DOOR -> PlayersHandler.UseDoor(slea, c.getPlayer());
            case DAMAGE_REACTOR -> PlayersHandler.HitReactor(slea, c);
            case TOUCH_REACTOR -> PlayersHandler.TouchReactor(slea, c);
            case CLOSE_CHALKBOARD -> c.getPlayer().setChalkboard(null);
            case ITEM_MAKER -> ItemMakerHandler.ItemMaker(slea, c);
            case ITEM_SORT -> {
                if (c.getPlayer().getMobVac() || c.getPlayer().getItemVac()) {
                    c.getPlayer().dropMessage(1, "开着挂机的时候不能操作");
                    break;
                }
                InventoryHandler.ItemSort(slea, c);
            }
            case ITEM_GATHER -> {
                if (c.getPlayer().getMobVac() || c.getPlayer().getItemVac()) {
                    c.getPlayer().dropMessage(1, "开着挂机的时候不能操作");
                    break;
                }
                InventoryHandler.ItemGather(slea, c);
            }
            case ITEM_MOVE -> InventoryHandler.ItemMove(slea, c);
            case ITEM_PICKUP -> InventoryHandler.PlayerPickup(slea, c, c.getPlayer());
            case USE_CASH_ITEM -> InventoryHandler.UseCashItem(slea, c);
            case USE_ITEM -> InventoryHandler.UseItem(slea, c, c.getPlayer());
            case USE_MAGNIFY_GLASS -> InventoryHandler.UseMagnify(slea, c);
            case USE_SCRIPTED_NPC_ITEM -> InventoryHandler.UseScriptedNPCItem(slea, c, c.getPlayer());
            case USE_RETURN_SCROLL -> InventoryHandler.UseReturnScroll(slea, c, c.getPlayer());
            case USE_UPGRADE_SCROLL -> {
                c.getPlayer().updateTick(slea.readInt());
                InventoryHandler.UseUpgradeScroll((byte) slea.readShort(), (byte) slea.readShort(), (byte) slea.readShort(), c, c.getPlayer());
            }
            case USE_POTENTIAL_SCROLL, USE_EQUIP_SCROLL -> {
                c.getPlayer().updateTick(slea.readInt());
                InventoryHandler.UseUpgradeScroll((byte) slea.readShort(), (byte) slea.readShort(), (byte) 0, c, c.getPlayer());
            }
            case USE_SUMMON_BAG -> InventoryHandler.UseSummonBag(slea, c, c.getPlayer());
            case USE_TREASUER_CHEST -> InventoryHandler.UseTreasureChest(slea, c, c.getPlayer());
            case USE_SKILL_BOOK -> InventoryHandler.UseSkillBook(slea, c, c.getPlayer());
            case USE_CATCH_ITEM -> InventoryHandler.UseCatchItem(slea, c, c.getPlayer());
            case USE_MOUNT_FOOD -> InventoryHandler.UseMountFood(slea, c, c.getPlayer());
            case REWARD_ITEM ->
                    InventoryHandler.UseRewardItem((byte) slea.readShort(), slea.readInt(), c, c.getPlayer());
            case HYPNOTIZE_DMG -> MobHandler.HypnotizeDmg(slea, c);
            case MOB_NODE -> MobHandler.handleMobNode(slea, c);
            case DISPLAY_NODE -> MobHandler.handleDisplayNode(slea, c);
            case MOVE_LIFE -> MobHandler.MoveMonster(slea, c);
            case AUTO_AGGRO -> MobHandler.handleAutoAggro(slea, c);
            case FRIENDLY_DAMAGE -> MobHandler.handleFriendlyDamage(slea, c);
            case MONSTER_BOMB -> MobHandler.handleMonsterBomb(slea, c);
            case QUEST_ACTION -> NPCHandler.QuestAction(slea, c, c.getPlayer());
            case NPC_SHOP -> NPCHandler.handleNPCShop(slea, c);
            case NPC_TALK -> NPCHandler.handleNPCTalk(slea, c, c.getPlayer());
            case NPC_TALK_MORE -> NPCHandler.NPCMoreTalk(slea, c);
            case NPC_ACTION -> NPCHandler.handleNPCAnimation(slea, c);
            case STORAGE -> NPCHandler.Storage(slea, c, c.getPlayer());
            case GENERAL_CHAT ->
                    ChatHandler.GeneralChat(slea.readMapleAsciiString(), slea.readByte(), c, c.getPlayer());
            case PARTYCHAT -> ChatHandler.Others(slea, c, c.getPlayer());
            case WHISPER -> ChatHandler.WhisperFind(slea, c);
            case MESSENGER -> ChatHandler.Messenger(slea, c);
            case AUTO_ASSIGN_AP -> StatsHandling.AutoAssignAP(slea, c, c.getPlayer());
            case DISTRIBUTE_AP -> StatsHandling.DistributeAP(slea, c, c.getPlayer());
            case DISTRIBUTE_SP -> {
                c.getPlayer().updateTick(slea.readInt());
                StatsHandling.DistributeSP(slea.readInt(), c, c.getPlayer());
            }
            case PLAYER_INTERACTION -> PlayerInteractionHandler.PlayerInteraction(slea, c, c.getPlayer());
            case GUILD_OPERATION -> GuildHandler.HandleGuild(slea, c);
            case UPDATE_CHAR_INFO -> PlayersHandler.UpdateCharInfo(slea, c, c.getPlayer());
            case DENY_GUILD_REQUEST -> {
                slea.skip(1);
                GuildHandler.denyGuildRequest(slea.readMapleAsciiString(), c);
            }
            case ALLIANCE_OPERATION -> AllianceHandler.HandleAlliance(slea, c, false);
            case DENY_ALLIANCE_REQUEST -> AllianceHandler.HandleAlliance(slea, c, true);
            case BBS_OPERATION -> BBSHandler.HandleBBS(slea, c);
            case PARTY_OPERATION -> PartyHandler.PartyOperatopn(slea, c);
            case DENY_PARTY_REQUEST -> PartyHandler.DenyPartyRequest(slea, c);
            case BUDDYLIST_MODIFY -> BuddyListHandler.BuddyOperationHandler(slea, c);
            case CYGNUS_SUMMON -> UserInterfaceHandler.CygnusSummonNPCRequest(c);
            case SHIP_OBJECT -> UserInterfaceHandler.ShipObjectRequest(slea.readInt(), c);
            case CASHSHOP_OPERATION -> CashShopOperation.BuyCashItem(slea, c, c.getPlayer());
            case COUPON_CODE -> {
                slea.skip(2);
                CashShopOperation.CouponCode(slea.readMapleAsciiString(), c);
            }
            case PARTY_SS -> ChatHandler.PARTY_SS(slea, c, c.getPlayer());
            case CS_UPDATE -> CashShopOperation.sendCashShopUpdate(c);
            case TOUCHING_MTS -> MTSOperation.MTSUpdate(MTSStorage.getInstance().getCart(c.getPlayer().getId()), c);
            case MTS_TAB -> MTSOperation.MTSOperation(slea, c);
            case DAMAGE_SUMMON -> {
                slea.skip(4);
                SummonHandler.DamageSummon(slea, c.getPlayer());
            }
            case MOVE_SUMMON -> SummonHandler.MoveSummon(slea, c.getPlayer());
            case SUMMON_ATTACK -> SummonHandler.SummonAttack(slea, c, c.getPlayer());
            case SPAWN_PET -> PetHandler.SpawnPet(slea, c, c.getPlayer());
            case MOVE_PET -> PetHandler.MovePet(slea, c.getPlayer());
            case PET_CHAT -> {
                if (slea.available() > 11L) {
                    PetHandler.PetChat((int) slea.readLong(), slea.readShort(), slea.readMapleAsciiString(), c.getPlayer());
                }
            }
            case PET_COMMAND -> PetHandler.PetCommand(slea, c, c.getPlayer());
            case PET_FOOD -> PetHandler.PetFood(slea, c, c.getPlayer());
            case PET_LOOT -> InventoryHandler.PetPickup(slea, c, c.getPlayer());
            case PET_AUTO_POT -> PetHandler.Pet_AutoPotion(slea, c, c.getPlayer());
            case MONSTER_CARNIVAL -> MonsterCarnivalHandler.MonsterCarnival(slea, c);
            case DUEY_ACTION -> DueyHandler.DueyOperation(slea, c);
            case USE_HIRED_MERCHANT -> HiredMerchantHandler.UseHiredMerchant(slea, c);
            case MERCH_ITEM_STORE -> HiredMerchantHandler.MerchantItemStore(slea, c);
            case LEFT_KNOCK_BACK -> PlayerHandler.leftKnockBack(slea, c);
            case SNOWBALL -> PlayerHandler.snowBall(slea, c);
            case REPAIR -> NPCHandler.repair(slea, c);
            case REPAIR_ALL -> NPCHandler.repairAll(c);
            case GAME_POLL -> UserInterfaceHandler.InGamePoll(slea, c);
            case OWL -> InventoryHandler.Owl(slea, c);
            case OWL_WARP -> InventoryHandler.OwlWarp(slea, c);
            case USE_OWL_MINERVA -> InventoryHandler.OwlMinerva(slea, c);
            case UPDATE_QUEST -> NPCHandler.UpdateQuest(slea, c);
            case USE_ITEM_QUEST -> NPCHandler.UseItemQuest(slea, c);
            case FOLLOW_REQUEST -> PlayersHandler.FollowRequest(slea, c);
            case FOLLOW_REPLY -> PlayersHandler.FollowReply(slea, c);
            case RING_ACTION -> PlayersHandler.RingAction(slea, c);
            case ITEM_UNLOCK -> PlayersHandler.UnlockItem(slea, c);
            case SOLOMON -> PlayersHandler.Solomon(slea, c);
            case GACH_EXP -> PlayersHandler.GachExp(slea, c);
            case REQUEST_FAMILY -> FamilyHandler.RequestFamily(slea, c);
            case OPEN_FAMILY -> FamilyHandler.OpenFamily(slea, c);
            case FAMILY_OPERATION -> FamilyHandler.FamilyOperation(slea, c);
            case PET_IGNORE -> PetHandler.PetIgnoreTag(slea, c, c.getPlayer());
            case DELETE_JUNIOR -> FamilyHandler.DeleteJunior(slea, c);
            case DELETE_SENIOR -> FamilyHandler.DeleteSenior(slea, c);
            case USE_FAMILY -> FamilyHandler.UseFamily(slea, c);
            case FAMILY_PRECEPT -> FamilyHandler.FamilyPrecept(slea, c);
            case FAMILY_SUMMON -> FamilyHandler.FamilySummon(slea, c);
            case ACCEPT_FAMILY -> FamilyHandler.AcceptFamily(slea, c);
            case CLIENT_ERROR -> log.error(slea.readMapleAsciiString());
            case SPECIAL_ATTACK -> PlayerHandler.SpecialAttack(slea, c, c.getPlayer());
            case BEANS_OPERATION -> BeanGame.BeansGameAction(slea, c);
            case BEANS_UPDATE -> BeanGame.updateBeans(slea, c);
            case LICENSE_REQUEST -> CharLoginHandler.LicenseRequest(slea, c);
            default -> log.warn("[发现未处理数据包] recv [{}]", header);
        }
    }

    static {
        BLOCKED = EnumSet.noneOf(RecvPacketOpcode.class);
        RecvPacketOpcode[] block = {RecvPacketOpcode.NPC_ACTION, RecvPacketOpcode.MOVE_PLAYER, RecvPacketOpcode.MOVE_PET, RecvPacketOpcode.MOVE_SUMMON, RecvPacketOpcode.MOVE_LIFE, RecvPacketOpcode.HEAL_OVER_TIME, RecvPacketOpcode.STRANGE_DATA};
        BLOCKED.addAll(Arrays.asList(block));
    }
}
